use crate::{
    core::core::ClientBuilder,
    napi::{auth::AuthType, file_info::FileInfo},
    types::{list_cmd::ListEntity, Auth, Depth},
};
use napi_derive_ohos::napi;
use anyhow::Error;
use napi_ohos::{bindgen_prelude::Buffer, Result};
use std::{
    fs::{self, File},
    io::Write,
};

#[napi]
pub struct Client {
    debug_mode: bool,
    password: String,
    user_name: String,
    webdav_url: String,
    auth_type: AuthType,
    client: Option<crate::core::core::Client>,
}

#[napi]
impl Client {
    #[napi(constructor)]
    pub fn new(
        webdav_url: String,
        user_name: Option<String>,
        password: Option<String>,
        auth_type: Option<AuthType>,
    ) -> Self {
        Self {
            webdav_url,
            client: None,
            debug_mode: false,
            password: password.unwrap_or_default(),
            user_name: user_name.unwrap_or_default(),
            auth_type: auth_type.unwrap_or(AuthType::Basic),
        }
    }

    #[napi]
    pub async unsafe fn connect(
        &mut self,
        debug_mode: bool,
        ignore_cert: bool,
        server_cert: Option<String>,
    ) -> Result<()> {
        self.debug_mode = debug_mode;
        let mut auth = Auth::Anonymous;
        if !self.user_name.is_empty() && !self.password.is_empty() {
            auth = match self.auth_type {
                AuthType::Basic => {
                    Auth::Basic(self.user_name.to_string(), self.password.to_string())
                }
                _ => Auth::Digest(self.user_name.to_string(), self.password.to_string()),
            };
        }
        let client = ClientBuilder::new()
            .set_host(self.webdav_url.to_string())
            .set_auth(auth)
            .build(debug_mode, ignore_cert, server_cert)?;
        self.client = Some(client);
        Ok(())
    }

    #[napi]
    pub async unsafe fn list(&mut self, remote_path: String) -> Result<Vec<FileInfo>> {
        let mut result = vec![];
        if let Some(client) = &self.client {
            let list = client.list(&remote_path, Depth::Infinity).await?;
            for item in list {
                if let ListEntity::File(file) = item {
                    result.push(FileInfo {
                        is_folder: false,
                        href: file.href.to_string(),
                        content_length: file.content_length,
                        content_type: Some(file.content_type.to_string()),
                    });
                } else if let ListEntity::Folder(folder) = item {
                    result.push(FileInfo {
                        is_folder: true,
                        content_length: 0,
                        content_type: None,
                        href: folder.href.to_string(),
                    });
                }
            }
        }
        Ok(result)
    }

    #[napi]
    pub async unsafe fn get(&mut self, remote_path: String) -> Result<Option<Buffer>> {
        let mut buffer: Option<Buffer> = None;
        if let Some(client) = &self.client {
            let bytes = client
                .get(&remote_path)
                .await?
                .bytes()
                .await
                .map_err(|e| Error::from(e))?;
            buffer = Some(Buffer::from(bytes.to_vec()));
        }
        Ok(buffer)
    }

    #[napi]
    pub async unsafe fn write_to(&mut self, remote_path: String, file_path: String) -> Result<()> {
        if let Some(client) = &self.client {
            let bytes = client
                .get(&remote_path)
                .await?
                .bytes()
                .await
                .map_err(|e| Error::from(e))?;
            let data = bytes.to_vec();
            if let Ok(mut file) = File::create(file_path) {
                for chunk in data.chunks(4096) {
                    file.write_all(&chunk)?;
                }
            }
        }
        Ok(())
    }

    #[napi]
    pub async unsafe fn put(&mut self, remote_path: String, file_path: String) -> bool {
        let mut result = false;
        if let Some(client) = &self.client {
            if let Ok(data) = fs::read(file_path) {
                let put_fn = client.put(&remote_path, data).await;
                if put_fn.is_ok() {
                    result = true;
                } else if self.debug_mode {
                    ohos_hilog_binding::hilog_info!(
                        "webdav put func not response! error: {:?}",
                        put_fn.as_ref().unwrap_err()
                    );
                }
            }
        }
        result
    }

    #[napi]
    pub async unsafe fn delete(&mut self, remote_path: String) -> bool {
        let mut result = false;
        if let Some(client) = &self.client {
            let delete_fn = client.delete(&remote_path).await;
            if delete_fn.is_ok() {
                result = true;
            } else if self.debug_mode {
                ohos_hilog_binding::hilog_info!(
                    "webdav delete func not response! error: {:?}",
                    delete_fn.as_ref().unwrap_err()
                );
            }
        }
        result
    }

    #[napi]
    pub async unsafe fn mv(&mut self, source_path: String, target_path: String) -> bool {
        let mut result = false;
        if let Some(client) = &self.client {
            let mv_fn = client.mv(&source_path, &target_path).await;
            if mv_fn.is_ok() {
                result = true;
            } else if self.debug_mode {
                ohos_hilog_binding::hilog_info!(
                    "webdav mv func not response! error: {:?}",
                    mv_fn.as_ref().unwrap_err()
                );
            }
        }
        result
    }

    #[napi]
    pub async unsafe fn cp(&mut self, source_path: String, target_path: String) -> bool {
        let mut result = false;
        if let Some(client) = &self.client {
            let cp_fn = client.cp(&source_path, &target_path).await;
            if cp_fn.is_ok() {
                result = true;
            } else if self.debug_mode {
                ohos_hilog_binding::hilog_info!(
                    "webdav cp func not response! error: {:?}",
                    cp_fn.as_ref().unwrap_err()
                );
            }
        }
        result
    }

    #[napi]
    pub async unsafe fn mkdir(&mut self, remote_path: String) -> bool {
        let mut result = false;
        if let Some(client) = &self.client {
            let mkdir_fn = client.mkcol(&remote_path).await;
            if mkdir_fn.is_ok() {
                result = true;
            } else if self.debug_mode {
                ohos_hilog_binding::hilog_info!(
                    "webdav mkdir func not response! error: {:?}",
                    mkdir_fn.as_ref().unwrap_err()
                );
            }
        }
        result
    }

    #[napi]
    pub fn close(&mut self) {
        if let Some(client) = self.client.take() {
            drop(client);
        }
    }
}

impl Drop for Client {
    fn drop(&mut self) {
        self.close();
    }
}
